Repository navigation
orchestrator implementation + persistence layer - #98
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughAdds a JSON-backed persistence layer (StorageAdapter + LowdbAdapter), storage types/config, orchestrators for environments/workspaces/processes/changes with cascade-delete behavior, comprehensive tests and docs, plus dependency and minor import/tsconfig adjustments. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Orch as Orchestrator
participant Store as StorageAdapter
participant Lowdb as LowdbAdapter
participant FS as Filesystem
Client->>Orch: create(entity)
Orch->>Orch: generate id, createdAt
Orch->>Store: set(collection, id, entity)
Store->>Lowdb: set(...)
Lowdb->>Lowdb: init() / deserializeDates()
Lowdb->>FS: read db.json
Lowdb->>Lowdb: serializeDates()
Lowdb->>FS: write db.json
Lowdb-->>Store: ok
Store-->>Orch: ok
Orch-->>Client: entity(with id)
sequenceDiagram
participant Client
participant WS as WorkspaceOrchestrator
participant Store as StorageAdapter
participant Lowdb as LowdbAdapter
participant FS as Filesystem
Client->>WS: delete(workspaceId)
rect rgb(220,240,255)
Note over WS,Store: Cascade: for each process -> delete agentSummaries then process
WS->>Store: getCollection("processes")
WS->>Store: delete("agentSummaries", ...) / delete("processes", ...)
end
rect rgb(240,220,255)
Note over WS,Store: Cascade: for each change -> delete fileDiffs then change
WS->>Store: getCollection("changes")
WS->>Store: delete("fileDiffs", ...) / delete("changes", ...)
end
rect rgb(255,235,220)
Note over WS,Store: Finally delete workspace
WS->>Store: delete("workspaces", workspaceId)
end
Store->>Lowdb: persist
Lowdb->>FS: write db.json
Lowdb-->>Store: ok
Store-->>WS: ok
WS-->>Client: ok
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/cli/package.json (1)
1-59: Fix three critical test failures blocking the PR.The tests are failing because of incompatibilities in test file setup:
apps/cli/test.js (line 2): Imports
"./source/app.js"which doesn't exist. The app is at./src/app.tsx. Change toimport App from "./src/app.tsx";All 5 test files in apps/cli/src/tests/: Import from
"bun:test"framework, but package.json test script runs AVA. Convert these to use AVA imports (import test from "ava") and AVA syntax (test()instead ofdescribe()andtest()), or switch the test runner to Bun in package.json.The test command runs AVA but encounters bun:test syntax, causing it to fail with exit code 1.
🧹 Nitpick comments (9)
apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (1)
1-10: Fix import-order lint warning via formatterCI reported
lint/source/organizeImportsfor this file. The current imports are already reasonably grouped, so this is likely just a formatting rule (e.g., sort alphabetically or group by module type). Rather than hand-tuning, I’d suggest running the project’s lint/format command locally (orbun lint --fixif available) to auto-fix the ordering here.apps/cli/src/lib/orchestrators/change-orchestrator.ts (1)
45-58: Consider sharing file-diff cascade logic with workspace/environment orchestratorsThe delete implementation performs a fileDiff cascade that is structurally similar to what
WorkspaceOrchestrator.deleteandEnvironmentOrchestrator.deleteWorkspaceCascadealready do when removing changes. To reduce duplication and keep cascade rules in sync, you may want to centralize this fileDiff deletion logic (e.g., a shared helper or a small internal utility) and call it from all three locations.apps/cli/PERSISTENCE_PLAN.md (1)
12-31: Align markdown fences and relationship diagram with implementationTwo small doc nits:
MD040 fenced-code warnings
The tree and relationship diagrams use bare triple backticks without a language. To satisfy markdownlint and improve readability, consider annotating them, e.g.:-
+text
apps/cli/src/
...... -``` +```text Environment (id) ...
AgentSummary parent in the diagram
The “Type Relationships” section currently showsAgentSummary (agentId)as a child ofChange, but the code cascadesagentSummariesfromProcess(seeProcessOrchestrator.deleteand the workspace/environment cascade helpers that filter bysummary.agentId === processId). To match the implementation, you may want something like:Environment (id) └── Workspace (environmentId) ├── Process (workspaceId) │ ├── Agent/Terminal │ └── AgentSummary (agentId) └── Change (workspaceId) └── FileDiff (changeId)Also applies to: 80-88
apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
65-111: Reduce duplication and repeated collection reads in cascade deleteThe
deletemethod correctly enforces cascade behavior, but there are two maintainability/perf concerns:
- The cascade logic (processes → agentSummaries, changes → fileDiffs) is nearly identical to
EnvironmentOrchestrator.deleteWorkspaceCascade, so any future change must be replicated in two places.- Within the loops,
getCollection("agentSummaries")andgetCollection("fileDiffs")are called for each process/change ID, which repeatedly reads the full collection.Consider:
- Extracting a shared internal helper (e.g., in a small
cascade.tsmodule or a private utility on a common base) that both orchestrators can reuse.- Hoisting the
getCollectioncalls out of the loops, then filtering in-memory for each ID:const agentSummaries = await this.storage.getCollection("agentSummaries"); // compute summaryIds per processId from this one snapshotThis keeps behavior the same while reducing duplication and I/O.
apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
37-51: Consolidate workspace cascade logic and avoid repeated full-collection readsThe environment orchestrator’s delete path is functionally sound and respects the cascade rules, but:
deleteWorkspaceCascadeis almost a copy ofWorkspaceOrchestrator.delete, which risks divergence if cascade behavior changes in one place but not the other.- Inside the process/change loops,
getCollection("agentSummaries")andgetCollection("fileDiffs")are called for each ID, repeatedly loading whole collections.To improve maintainability and efficiency, consider:
- Extracting shared cascade helpers (e.g.,
deleteWorkspaceWithChildren(storage, workspaceId)) and calling those from bothEnvironmentOrchestratorandWorkspaceOrchestrator.- Hoisting
getCollection("agentSummaries")/getCollection("fileDiffs")outside of the per-ID loops and filtering from a single snapshot.The external behavior stays identical but will be easier to evolve.
Also applies to: 53-67, 69-116
apps/cli/src/lib/orchestrators/process-orchestrator.ts (2)
79-115: Prefer usingtypeto detect agents instead of"status" in updatedUsing
"status" in updatedworks today because only agents have astatusfield, but it’s a bit brittle if other process types ever gain astatus-like property.You can make the intent clearer and safer by checking
type:- // Update status for agents - if ("status" in updated) { - (updated as Agent).status = "stopped"; - } + // Update status for agents + if (existing.type === ProcessType.AGENT) { + (updated as Agent).status = "stopped"; + }and similarly in
stopAll, useprocess.type === ProcessType.AGENTto decide whether to setstatus.
117-130: Batch agent summary deletions to reduce redundant reads/writes
deletecallsstorage.deleteonce per relatedagentSummary. With the currentLowdbAdapter, each delete incurs a full read + write of the JSON file, which can be expensive if a process has many summaries.A more efficient pattern is:
- Load
agentSummariesonce.- Remove matching entries in-memory.
- Call
updateCollection("agentSummaries", updatedSummaries)once.This keeps behavior the same while reducing I/O.
apps/cli/src/lib/storage/types.ts (1)
37-44: Consider future‑proofingSerialized<T>forDate | nullfieldsRight now the conditional covers
DateandDate | undefined, which matches current models. If you later introduceDate | nullfields, they won't be serialized as strings by this helper.If that becomes relevant, you could extend the condition to include
Date | nullas well.apps/cli/src/lib/storage/lowdb-adapter.ts (1)
93-167: Potential I/O optimization and concurrency refinement (non‑blocking)Each mutating method (
set,delete, per‑entity operations in orchestrators) doesread → mutate → write. With higher write volumes, you may want to:
- Batch logical operations at the orchestrator level (e.g., deleting many summaries with a single
updateCollection), and/or- Introduce a simple
initPromiseto ensure only oneJSONFilePresetinstance is constructed even under concurrent calls.Not required for correctness, but worth considering if CLI usage grows.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (18)
apps/cli/PERSISTENCE_PLAN.md(1 hunks)apps/cli/package.json(1 hunks)apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/change-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/environment-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/index.ts(1 hunks)apps/cli/src/lib/orchestrators/process-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts(1 hunks)apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts(1 hunks)apps/cli/src/lib/storage/adapter.ts(1 hunks)apps/cli/src/lib/storage/config.ts(1 hunks)apps/cli/src/lib/storage/index.ts(1 hunks)apps/cli/src/lib/storage/lowdb-adapter.ts(1 hunks)apps/cli/src/lib/storage/types.ts(1 hunks)apps/cli/src/types/index.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts (4)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(14-168)apps/cli/src/types/environment.ts (1)
Environment(1-3)apps/cli/src/types/process.ts (1)
Process(8-18)apps/cli/src/types/change.ts (1)
Change(1-6)
apps/cli/src/lib/orchestrators/environment-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-70)apps/cli/src/types/environment.ts (1)
Environment(1-3)
apps/cli/src/lib/storage/adapter.ts (1)
apps/cli/src/lib/storage/types.ts (1)
Database(15-22)
apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-70)apps/cli/src/types/workspace.ts (1)
LocalWorkspace(12-15)
apps/cli/src/lib/storage/types.ts (3)
apps/cli/src/types/environment.ts (1)
Environment(1-3)apps/cli/src/types/process.ts (2)
Process(8-18)Agent(29-32)apps/cli/src/types/change.ts (2)
Change(1-6)AgentSummary(25-30)
apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts (4)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(14-168)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
WorkspaceOrchestrator(14-112)apps/cli/src/lib/orchestrators/change-orchestrator.ts (1)
ChangeOrchestrator(12-59)apps/cli/src/types/workspace.ts (1)
LocalWorkspace(12-15)
apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (4)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(14-168)apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
EnvironmentOrchestrator(12-117)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
WorkspaceOrchestrator(14-112)apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)
ProcessOrchestrator(17-131)
apps/cli/src/lib/storage/lowdb-adapter.ts (3)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-70)apps/cli/src/lib/storage/types.ts (3)
SerializedDatabase(28-35)createEmptyDatabase(57-64)Database(15-22)apps/cli/src/lib/storage/config.ts (2)
getDbPath(23-25)ensureStorageDir(31-37)
apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts (5)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(14-168)apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)
ProcessOrchestrator(17-131)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
WorkspaceOrchestrator(14-112)apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
EnvironmentOrchestrator(12-117)apps/cli/src/types/process.ts (1)
Agent(29-32)
apps/cli/src/lib/orchestrators/process-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-70)apps/cli/src/types/process.ts (3)
Process(8-18)Agent(29-32)Terminal(20-22)
apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts (4)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(14-168)apps/cli/src/lib/orchestrators/change-orchestrator.ts (1)
ChangeOrchestrator(12-59)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
WorkspaceOrchestrator(14-112)apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
EnvironmentOrchestrator(12-117)
apps/cli/src/lib/orchestrators/change-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-70)apps/cli/src/types/change.ts (1)
Change(1-6)
🪛 GitHub Actions: CI
apps/cli/PERSISTENCE_PLAN.md
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/package.json
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/storage/config.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/environment-orchestrator.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/storage/adapter.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/workspace-orchestrator.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/storage/types.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/index.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts
[warning] 1-1: lint/source/organizeImports FIXABLE: The imports and exports are not sorted.
apps/cli/src/lib/storage/lowdb-adapter.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/storage/index.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/process-orchestrator.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts
[warning] 1-1: lint/source/organizeImports FIXABLE: The imports and exports are not sorted.
apps/cli/src/lib/orchestrators/change-orchestrator.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
apps/cli/src/types/index.ts
[error] 1-1: cli#test: bun run test exited with code 1 for path '/home/runner/work/superset/superset/apps/cli'.
🪛 markdownlint-cli2 (0.18.1)
apps/cli/PERSISTENCE_PLAN.md
12-12: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
80-80: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (13)
apps/cli/src/types/index.ts (1)
1-4: LGTM!The addition of Change types export and reordering of exports are appropriate for the new persistence layer.
apps/cli/src/lib/storage/config.ts (1)
1-37: LGTM! Good security practices.The storage configuration is well-implemented with:
- Environment variable override for flexibility
- Restrictive permissions (0o700) for the storage directory
- Proper path construction and directory creation
The
existsSynccheck followed bymkdirwithrecursive: trueis safe as Node.js handles concurrent creation attempts gracefully.apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts (1)
1-287: LGTM! Comprehensive test coverage.The test suite is well-structured with:
- Proper setup/teardown using temporary directories
- Comprehensive CRUD operation coverage
- Cascade deletion validation
- Edge case handling (non-existent processes, already stopped processes)
- Good test isolation
apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts (1)
1-199: LGTM! Well-structured test suite.Excellent test coverage with:
- Both workspace types (local/cloud) tested
- Cascade deletion verified through multiple levels (workspace → changes → fileDiffs)
- Proper filtering tests
- Clean setup/teardown
apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts (1)
11-250: LGTM! Thorough cascade deletion tests.The test suite provides excellent coverage including:
- Change creation with proper ID/timestamp generation
- Workspace-filtered listing
- Update validation with error handling for non-existent changes
- Multi-level cascade deletion (change → fileDiffs)
- Isolation tests ensuring deletion only affects related entities
apps/cli/src/lib/orchestrators/index.ts (1)
1-4: LGTM!Clean barrel export providing a unified API surface for all orchestrators.
apps/cli/src/lib/storage/index.ts (1)
1-5: LGTM!Well-organized barrel export exposing the complete storage API surface including types, adapters, and utilities.
apps/cli/package.json (1)
26-26: lowdb version verified as latest and secure.Verification confirms that lowdb@7.0.1 is the current latest stable release with no known security vulnerabilities. The dependency specification is appropriate.
apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts (1)
29-44: LowdbAdapter tests look comprehensive and aligned with behaviorThe test suite does a good job covering initialization, CRUD, collection-level operations, date serialization (including optional and “nested” dates), and simple concurrent writes with a shared adapter instance and temp-file isolation. The cleanup logic around
mkdtemp/rmis also sound.No changes needed here from my side.
Also applies to: 46-116, 118-171, 173-184
apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)
13-37: Nice, cohesive orchestrator abstraction over the storage adapterThe overall shape (typed constructor,
get/list/createusingStorageAdapterand domain types) is clean and aligns well with the new persistence layer.apps/cli/src/lib/storage/adapter.ts (1)
7-70: StorageAdapter surface looks well‑factored and sufficientThe interface cleanly separates database-wide operations from collection/entity-level CRUD and matches how the orchestrators and LowdbAdapter use it. No issues from a type or responsibility standpoint.
apps/cli/src/lib/storage/types.ts (1)
15-64: Typed storage schema and serialization helpers are consistent with domain types
Database/SerializedDatabaseplus theSerialized<T>helper map the current Date-bearing entities correctly, andcreateEmptyDatabaseprovides a clear, typed initializer for persistence.apps/cli/src/lib/storage/lowdb-adapter.ts (1)
14-34: LowdbAdapter wiring and lazy initialization align with StorageAdapterThe adapter correctly wraps the JSON preset, uses
createEmptyDatabasefor defaults, and lazily initializes withensureStorageDirand a storeddbPath. The public methods consistently callinitbefore use.
| test("throws error for non-existent environment", async () => { | ||
| expect(orchestrator.get("non-existent")).rejects.toThrow( | ||
| "Environment with id non-existent not found", | ||
| ); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
Await .rejects expectations in async tests
All tests using expect(...).rejects.toThrow(...) are async but never await the expectation (e.g., Lines 56, 91, 99, 116, 133, 154). This means the rejection assertions may not be executed before the test completes and can lead to false positives or unhandled rejections.
Consider updating them like:
- test("throws error for non-existent environment", async () => {
- expect(orchestrator.get("non-existent")).rejects.toThrow(
- "Environment with id non-existent not found",
- );
- });
+ test("throws error for non-existent environment", async () => {
+ await expect(orchestrator.get("non-existent")).rejects.toThrow(
+ "Environment with id non-existent not found",
+ );
+ });Apply the same pattern to:
- Line 93:
orchestrator.update("non-existent", { id: "new" }) - Line 103:
orchestrator.get(env.id) - Line 116:
workspaceOrchestrator.get(workspace.id) - Line 133:
processOrchestrator.get(process.id) - Lines 154–155:
workspaceOrchestrator.get(ws1.id)/get(ws2.id)
You can re-run the CLI tests after this change:
Also applies to: 91-95, 99-104, 106-117, 119-135, 136-157
🏁 Script executed:
#!/bin/bash
cd apps/cli
bun test --filter EnvironmentOrchestratorLength of output: 41
Let me examine the test file directly to verify the issue.
🏁 Script executed:
#!/bin/bash
# Read the test file to verify the async/await pattern issue
cat apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.tsLength of output: 4822
Await .rejects expectations in async tests
All tests using expect(...).rejects.toThrow() are async but never await the expectation. This means rejection assertions may not execute before the test completes, causing false positives or unhandled rejections. The issue affects:
- Line 56–60:
orchestrator.get("non-existent") - Line 93–97:
orchestrator.update("non-existent", { id: "new" }) - Line 103–109:
orchestrator.get(env.id) - Line 113–114:
workspaceOrchestrator.get(workspace.id) - Line 125–126:
processOrchestrator.get(process.id) - Line 142–144:
workspaceOrchestrator.get(ws1.id)andget(ws2.id)
Add await before each expect(...).rejects:
- expect(orchestrator.get("non-existent")).rejects.toThrow(
+ await expect(orchestrator.get("non-existent")).rejects.toThrow(Apply this pattern to all six locations listed above.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| test("throws error for non-existent environment", async () => { | |
| expect(orchestrator.get("non-existent")).rejects.toThrow( | |
| "Environment with id non-existent not found", | |
| ); | |
| }); | |
| test("throws error for non-existent environment", async () => { | |
| await expect(orchestrator.get("non-existent")).rejects.toThrow( | |
| "Environment with id non-existent not found", | |
| ); | |
| }); |
🤖 Prompt for AI Agents
In apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts
around lines 56–60 (and also update the other listed locations: lines 93–97,
103–109, 113–114, 125–126, 142–144), the tests use expect(...).rejects.toThrow()
without awaiting the expectation which can lead to false positives; fix by
prefixing each expect(...).rejects... with await (e.g. await
expect(orchestrator.get("non-existent")).rejects.toThrow(...)) so the test
runner waits for the rejection assertions to complete.
| async create( | ||
| change: Omit<Change, "id" | "timestamp">, | ||
| ): Promise<Change> { | ||
| const newChange: Change = { | ||
| ...change, | ||
| id: randomUUID(), | ||
| createdAt: new Date(), | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
Fix create parameter type to match Change shape
Change is defined with createdAt: Date and no timestamp property, but the create signature is:
async create(
change: Omit<Change, "id" | "timestamp">,
): Promise<Change> {This has two issues:
timestampdoesn’t exist onChange, so the type is misleading.- The
Changepayload still includescreatedAt, even thoughcreatealways setscreatedAt: new Date()itself. Callers are forced to provide a field that is ignored.
A more accurate signature is:
- async create(
- change: Omit<Change, "id" | "timestamp">,
- ): Promise<Change> {
+ async create(
+ change: Omit<Change, "id" | "createdAt">,
+ ): Promise<Change> {This way callers only supply workspaceId and summary, and create owns ID and timestamp generation.
Please also verify that the ChangeOrchestrator interface in apps/cli/src/types/change.ts reflects this corrected signature and adjust tests accordingly.
I'll verify the review comment by examining the relevant type definitions and interface implementations in the codebase.
🏁 Script executed:
#!/bin/bash
# Find and examine the Change type definition
echo "=== Searching for Change type definition ==="
rg -n "type Change|interface Change" --type=ts
echo -e "\n=== Examining change.ts type file ==="
fd -n "change.ts" --type=f | grep -E "types/change"
echo -e "\n=== Searching for ChangeOrchestrator interface ==="
rg -n "interface ChangeOrchestrator|type ChangeOrchestrator" --type=ts
echo -e "\n=== Examining change-orchestrator.ts implementation ==="
fd -n "change-orchestrator.ts" --type=f | head -1Length of output: 851
🏁 Script executed:
#!/bin/bash
# Get detailed content of type definitions
echo "=== Change type from types/change.ts ==="
find . -path "./apps/cli/src/types/change.ts" -type f -exec cat {} \;
echo -e "\n=== ChangeOrchestrator interface from types/change.ts ==="
find . -path "./apps/cli/src/types/change.ts" -type f -exec grep -A 20 "interface ChangeOrchestrator" {} \;
echo -e "\n=== Change orchestrator create method ==="
cat ./apps/cli/src/lib/orchestrators/change-orchestrator.ts | head -40Length of output: 2501
🏁 Script executed:
#!/bin/bash
# Search for usages of create method and tests
echo "=== Tests related to change orchestrator ==="
fd -n ".*\.test\.|.*\.spec\." --type=f | xargs rg -l "create.*change|ChangeOrchestrator" 2>/dev/null | head -5
echo -e "\n=== Usages of orchestrator.create ==="
rg -n "\.create\(" --type=ts -B 3 -A 1 ./apps/cli/src/lib/orchestrators/change-orchestrator.ts
echo -e "\n=== Callers of orchestrator.create ==="
rg -n "changeOrchestrator\.create|orchestrator\.create" --type=ts -B 2 -A 2 | head -40Length of output: 4435
🏁 Script executed:
#!/bin/bash
# Examine the change orchestrator test file
echo "=== Change orchestrator tests ==="
cat ./apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.tsLength of output: 7010
🏁 Script executed:
#!/bin/bash
# Search for actual calls to changeOrchestrator.create() with parameters
echo "=== Calls to changeOrchestrator.create with parameters ==="
rg -n "changeOrchestrator\.create\(" --type=ts -A 3 -B 1Length of output: 1109
Fix create parameter type in both interface and implementation to use "createdAt" instead of non-existent "timestamp"
The Change type has createdAt: Date but no timestamp property. Both the ChangeOrchestrator interface and its implementation incorrectly specify:
create: (change: Omit<Change, "id" | "timestamp">) => Promise<Change>;This creates two problems:
- Referencing a non-existent
timestampkey is misleading - Callers are forced to pass
createdAt, which the implementation ignores and always overwrites withnew Date()
Required fixes:
- apps/cli/src/types/change.ts (line 33): Change interface signature from
Omit<Change, "id" | "timestamp">toOmit<Change, "id" | "createdAt"> - apps/cli/src/lib/orchestrators/change-orchestrator.ts (line 24): Update implementation signature to match
Existing tests already pass the correct structure ({workspaceId, summary, createdAt}), so they will continue to work once the interface is corrected.
🤖 Prompt for AI Agents
In apps/cli/src/types/change.ts around line 33 and
apps/cli/src/lib/orchestrators/change-orchestrator.ts around line 24, the create
method parameter uses Omit<Change, "id" | "timestamp"> but Change defines
createdAt (not timestamp) and the implementation overwrites createdAt anyway;
update both the interface signature and the implementation signature to use
Omit<Change, "id" | "createdAt"> so callers are not misled and the types align
with the actual Change shape and behavior.
| private deserializeDates<T>(obj: any): T { | ||
| if (obj === null || obj === undefined) return obj; | ||
|
|
||
| if (typeof obj === "string" && this.isISODate(obj)) { | ||
| return new Date(obj) as any; | ||
| } | ||
|
|
||
| if (Array.isArray(obj)) { | ||
| return obj.map((item) => this.deserializeDates(item)) as any; | ||
| } | ||
|
|
||
| if (typeof obj === "object") { | ||
| const result: any = {}; | ||
| for (const [key, value] of Object.entries(obj)) { | ||
| result[key] = this.deserializeDates(value); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| return obj; | ||
| } | ||
|
|
||
| /** | ||
| * Serialize dates to ISO strings | ||
| */ | ||
| private serializeDates<T>(obj: T): any { | ||
| if (obj === null || obj === undefined) return obj; | ||
|
|
||
| if (obj instanceof Date) { | ||
| return obj.toISOString(); | ||
| } | ||
|
|
||
| if (Array.isArray(obj)) { | ||
| return obj.map((item) => this.serializeDates(item)); | ||
| } | ||
|
|
||
| if (typeof obj === "object") { | ||
| const result: any = {}; | ||
| for (const [key, value] of Object.entries(obj)) { | ||
| result[key] = this.serializeDates(value); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| return obj; | ||
| } |
There was a problem hiding this comment.
ISO‑date heuristic can accidentally convert user strings into Date objects
deserializeDates converts any string matching the ISO pattern into a Date, regardless of which field it belongs to. This means a user‑provided field (e.g. a summary string) that happens to be "2025-01-01T00:00:00.000Z" would come back as a Date instead of a string, violating the domain type.
To tighten this up, consider one of:
- Restricting conversion to known date field names (e.g.
createdAt,updatedAt,endedAt) by passing the key intodeserializeDatesand checking it, or - Using more explicit per-entity serializers/deserializers that know exactly which fields are dates.
That keeps the generic helper from surprising consumers on edge-case values.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (9)
apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (6)
56-60: Missingawaiton rejection assertionThis test will complete before the rejection is verified, potentially causing false positives.
91-95: Missingawaiton rejection assertionThis test will complete before the rejection is verified, potentially causing false positives.
99-104: Missingawaiton rejection assertionThis test will complete before the rejection is verified, potentially causing false positives.
106-117: Missingawaiton rejection assertion at line 116This test will complete before the rejection is verified, potentially causing false positives.
119-134: Missingawaiton rejection assertion at line 133This test will complete before the rejection is verified, potentially causing false positives.
136-157: Missingawaiton rejection assertions at lines 154-155These tests will complete before the rejections are verified, potentially causing false positives.
apps/cli/src/lib/orchestrators/change-orchestrator.ts (1)
22-31: Fix parameter type to use"createdAt"instead of non-existent"timestamp"The
Changetype hascreatedAt: Datebut the signature references"timestamp"which doesn't exist.apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)
69-77: Guard against updating theidfield to prevent storage key mismatchThe update method allows
updates.idto overwrite the stored id field while the storage key remains unchanged, causing inconsistency.apps/cli/src/lib/storage/lowdb-adapter.ts (1)
45-93: ISO‑string date deserialization is overly broad and may change user data typesThis helper will convert any ISO‑looking string anywhere in the object graph into a
Date, not just known date fields. That means a user field (e.g. a summary or label) that happens to be"2025-01-01T00:00:00.000Z"will round‑trip as aDateinstead of a string, violating consumers’ expectations.Consider restricting conversion to explicit date fields (e.g.
createdAt,updatedAt,endedAt) or using per-entity serializers/deserializers that know exactly which properties are dates.
🧹 Nitpick comments (6)
apps/cli/test.js (1)
2-4: Cosmetic import reordering unrelated to PR objectives.This change only reorders imports with no functional impact. It appears unrelated to the persistence layer additions described in the PR objectives. Consider whether this was an accidental inclusion.
apps/cli/src/lib/persistence_docs/PLAN.md (1)
12-31: Consider adding language identifiers to code blocks for better renderingAdding language specifiers helps with syntax highlighting and editor support.
Apply this pattern:
-``` +```text apps/cli/src/ ...And for the TypeScript interface:
-```typescript +```typescript interface Database { ... }Also applies to: 52-61, 80-88
apps/cli/src/lib/persistence_docs/SUMMARY.md (1)
11-30: Consider adding language identifiers to diagram code blocksAdding
textas the language identifier improves markdown rendering consistency.Example:
-``` +```text ┌─────────────────────────────────────────┐ │ Orchestrators │ ...Also applies to: 36-57, 76-84
apps/cli/src/lib/storage/lowdb-adapter.ts (3)
17-43: Constructor and lazy initialization look solid; directory creation can be simplifiedThe lazy
initplus configurabledbPathis clean. You can drop theexistsSynccheck and just callmkdir(parentDir, { recursive: true, mode: 0o700 })unconditionally—simplifies the code, avoids a TOCTOU check, and removes a sync FS call.
115-121: getCollection() is correct; consider the cost of full read + deep cloneThe implementation is functionally sound, but every call does a full
read()and deepdeserializeDatesof the collection. For larger datasets or hot paths, you might eventually want a lighter-weight path (e.g. caching or narrower deserialization) if performance becomes an issue.
164-171: has() works; considerhasOwnPropertyif you ever change collection prototypesUsing
id in this.db!.data[collection]is fine given collections are plain object literals. If you ever move toObject.create(null)or tweak prototypes, a more defensive pattern would be:return Object.prototype.hasOwnProperty.call(this.db!.data[collection], id);Not necessary now, just a future-proofing option.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
apps/cli/server.ts(1 hunks)apps/cli/src/cli.tsx(1 hunks)apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/change-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/environment-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/process-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts(1 hunks)apps/cli/src/lib/persistence_docs/EXAMPLE.md(1 hunks)apps/cli/src/lib/persistence_docs/PLAN.md(1 hunks)apps/cli/src/lib/persistence_docs/SUMMARY.md(1 hunks)apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts(1 hunks)apps/cli/src/lib/storage/adapter.ts(1 hunks)apps/cli/src/lib/storage/lowdb-adapter.ts(1 hunks)apps/cli/test.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/cli/src/lib/orchestrators/tests/process-orchestrator.test.ts
- apps/cli/src/lib/orchestrators/tests/workspace-orchestrator.test.ts
- apps/cli/src/lib/orchestrators/tests/change-orchestrator.test.ts
- apps/cli/src/lib/orchestrators/workspace-orchestrator.ts
🧰 Additional context used
🧬 Code graph analysis (7)
apps/cli/src/lib/orchestrators/environment-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-62)apps/cli/src/types/environment.ts (1)
Environment(1-3)
apps/cli/src/lib/orchestrators/change-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-62)apps/cli/src/types/change.ts (1)
Change(1-6)
apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts (4)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(17-178)apps/cli/src/types/environment.ts (1)
Environment(1-3)apps/cli/src/types/process.ts (1)
Process(8-18)apps/cli/src/types/change.ts (1)
Change(1-6)
apps/cli/src/lib/orchestrators/process-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-62)apps/cli/src/types/process.ts (3)
Process(8-18)Agent(29-32)Terminal(20-22)
apps/cli/src/lib/storage/adapter.ts (1)
apps/cli/src/lib/storage/types.ts (1)
Database(15-22)
apps/cli/src/lib/storage/lowdb-adapter.ts (3)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-62)apps/cli/src/lib/storage/types.ts (3)
SerializedDatabase(28-35)createEmptyDatabase(57-64)Database(15-22)apps/cli/src/lib/storage/config.ts (1)
getDbPath(23-25)
apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts (4)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(17-178)apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
EnvironmentOrchestrator(12-113)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
WorkspaceOrchestrator(14-111)apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)
ProcessOrchestrator(17-131)
🪛 markdownlint-cli2 (0.18.1)
apps/cli/src/lib/persistence_docs/PLAN.md
12-12: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
80-80: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
apps/cli/src/lib/persistence_docs/SUMMARY.md
11-11: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
36-36: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
76-76: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (13)
apps/cli/server.ts (1)
2-2: LGTM!The import reordering (type imports before value imports) is a harmless style adjustment with no functional impact.
apps/cli/src/cli.tsx (1)
4-4: Import reordering is harmless.The React import has been moved to appear after the meow import. This change has no functional impact and appears unrelated to the PR's persistence layer objectives.
apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts (1)
1-187: LGTM! Comprehensive test coverageThe test suite thoroughly validates the LowdbAdapter with proper setup/teardown, CRUD operations, date serialization edge cases, and concurrent writes. All async operations are properly awaited.
apps/cli/src/lib/persistence_docs/EXAMPLE.md (1)
1-303: LGTM! Well-structured usage documentationThe examples provide clear guidance on common workflows, error handling, testing setup, and migration paths. All examples align with the implemented API.
apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
1-113: LGTM! Well-implemented cascade deletion logicThe EnvironmentOrchestrator properly implements CRUD operations with comprehensive cascade deletion following the correct order: agentSummaries → processes → fileDiffs → changes → workspaces → environment. The update method correctly handles ID changes by managing storage keys.
apps/cli/src/lib/storage/adapter.ts (1)
7-61: StorageAdapter interface is well-shaped and aligns with the Database modelThe async CRUD surface is coherent, type-safe against
Database, and gives a clean abstraction for pluggable backends. No changes needed from my side here.apps/cli/src/lib/storage/lowdb-adapter.ts (7)
95-101: ISO date regex is precise and appropriately narrowThe regex only matches full UTC ISO timestamps with optional milliseconds, which is a good balance between correctness and avoiding accidental matches on partial date strings.
103-107: read() correctly initializes, reloads, and normalizes datesThe
readimplementation does lazy init, reloads from disk, and runs throughdeserializeDatesto return a fully-typedDatabase. This matches the StorageAdapter contract well.
109-113: write() correctly serializes dates and persists atomically via LowdbSerializing dates before assigning to
db.dataand then callingwrite()is straightforward and keeps on-disk data in the serialized shape expected by Lowdb.
123-131: updateCollection() matches the intended semanticsReading before mutation, serializing, and then writing the updated collection back is clear and correct for a Lowdb-backed
Record<string, Entity>collection.
143-151: set() implementation is straightforward and consistent with updateCollection()The logic (init → read → serialize value → assign → write) is predictable and consistent with the collection update behavior.
154-161: delete() implementation is correct for Record-based collectionsUsing
deleteon the in-memoryRecord<string, Entity>followed bywrite()is appropriate here; no issues spotted.
173-177: clear() correctly resets the database using the shared factoryReinitializing
db.dataviacreateEmptyDatabase()and writing it back is clean and works well for tests and “reset” behavior.
| async get<K extends keyof Database>( | ||
| collection: K, | ||
| id: string, | ||
| ): Promise<Database[K][string] | undefined> { | ||
| await this.init(); | ||
| await this.db!.read(); | ||
| const item = this.db!.data[collection][id]; | ||
| return item ? this.deserializeDates(item) : undefined; | ||
| } |
There was a problem hiding this comment.
Avoid truthiness check when deciding whether an item exists
item ? this.deserializeDates(item) : undefined will treat any falsy stored value (0, false, "") as “not found”. Today your entities are objects so this is fine, but for robustness it’s safer to check explicitly:
- const item = this.db!.data[collection][id];
- return item ? this.deserializeDates(item) : undefined;
+ const item = this.db!.data[collection][id];
+ return item === undefined ? undefined : this.deserializeDates(item);🤖 Prompt for AI Agents
In apps/cli/src/lib/storage/lowdb-adapter.ts around lines 133 to 141, the code
uses a truthiness check (item ? this.deserializeDates(item) : undefined) which
treats falsy stored values like 0, false, or "" as “not found”; change the
conditional to explicitly check for null/undefined (e.g., if (item !== undefined
&& item !== null) return this.deserializeDates(item); else return undefined) so
valid falsy values are returned correctly while still returning undefined only
for missing entries.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts (1)
155-159: Remove redundantcreatedAtfield from test data.The
createdAtfield is automatically set byChangeOrchestrator.create()and the passed value is ignored. Including it in the test data may confuse readers into thinking the provided timestamp is used.Apply this diff to remove the redundant field:
const change = await changeOrchestrator.create({ workspaceId: workspace.id, summary: "Test change", - createdAt: new Date(), });Also applies to: 174-178
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
apps/cli/package.json(2 hunks)apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts(1 hunks)apps/cli/src/lib/orchestrators/change-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/environment-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/process-orchestrator.ts(1 hunks)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts(1 hunks)apps/cli/tsconfig.json(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/cli/src/lib/orchestrators/tests/environment-orchestrator.test.ts
- apps/cli/src/lib/orchestrators/change-orchestrator.ts
- apps/cli/src/lib/orchestrators/workspace-orchestrator.ts
- apps/cli/src/lib/orchestrators/tests/change-orchestrator.test.ts
- apps/cli/src/lib/orchestrators/environment-orchestrator.ts
🧰 Additional context used
🧬 Code graph analysis (3)
apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts (5)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(17-178)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
WorkspaceOrchestrator(14-115)apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
EnvironmentOrchestrator(12-110)apps/cli/src/lib/orchestrators/change-orchestrator.ts (1)
ChangeOrchestrator(12-60)apps/cli/src/types/workspace.ts (1)
LocalWorkspace(12-15)
apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts (5)
apps/cli/src/lib/storage/lowdb-adapter.ts (1)
LowdbAdapter(17-178)apps/cli/src/lib/orchestrators/process-orchestrator.ts (1)
ProcessOrchestrator(17-135)apps/cli/src/lib/orchestrators/workspace-orchestrator.ts (1)
WorkspaceOrchestrator(14-115)apps/cli/src/lib/orchestrators/environment-orchestrator.ts (1)
EnvironmentOrchestrator(12-110)apps/cli/src/types/process.ts (1)
Agent(29-32)
apps/cli/src/lib/orchestrators/process-orchestrator.ts (2)
apps/cli/src/lib/storage/adapter.ts (1)
StorageAdapter(7-62)apps/cli/src/types/process.ts (3)
Process(8-18)Agent(29-32)Terminal(20-22)
🔇 Additional comments (10)
apps/cli/tsconfig.json (1)
7-7: LGTM: Bun types configuration aligned with storage layer migration.Adding Bun type definitions to the compiler options correctly enables TypeScript type resolution for the Bun runtime environment that the new persistence layer (lowdb-adapter) and orchestrators depend on.
apps/cli/package.json (2)
26-26: LGTM: Appropriate runtime dependency for persistence layer.
lowdbis a lightweight JSON database well-suited for the persistence layer (StorageAdapter + LowdbAdapter) introduced in this PR. The semantic version constraint (^7.0.1) is reasonable.
35-35: bun-types@1.3.2 is verified as the latest stable version with no known vulnerabilities.bun-types 1.3.2 is the latest stable version as of Nov 18, 2025, and there are no known direct security vulnerabilities affecting this version in public vulnerability databases. The dependency specification is correct and up-to-date.
apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts (1)
1-196: Comprehensive test coverage for WorkspaceOrchestrator.The test suite thoroughly covers CRUD operations, filtering, error handling, and cascade deletion behavior. Test isolation via temporary storage is properly implemented.
apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts (3)
170-198: Excellent test for immutable field protection.This test effectively verifies that immutable fields (
id,workspaceId,createdAt) cannot be modified through theupdatemethod, which is essential for maintaining data integrity.
267-288: Well-designed idempotence test.This test ensures
stopAlldoesn't modify already-stopped processes, which is important for avoiding unintended timestamp updates and maintaining accurate audit trails.
1-335: Comprehensive test coverage for ProcessOrchestrator.The test suite thoroughly covers all lifecycle operations (create, get, list, update, stop, stopAll, delete), cascade deletion behavior, and edge cases like idempotence. The test structure demonstrates best practices with proper isolation and cleanup.
apps/cli/src/lib/orchestrators/process-orchestrator.ts (3)
69-81: Immutable field protection successfully implemented.The past review comment has been addressed. The
updatemethod now properly filters out immutable fields (id,workspaceId,createdAt) before merging updates, preventing record desynchronization.
91-94: Duck-typing approach for agent detection is acceptable.The
"status" in updatedcheck effectively distinguishes agents from other process types. While checkingprocess.type === ProcessType.AGENTwould be more explicit, the current approach is valid given the type hierarchy and is consistently applied.Also applies to: 112-114
1-135: Well-structured orchestrator with proper CRUD and lifecycle management.The implementation correctly handles process lifecycle operations, maintains immutable field integrity, and implements cascade deletion for related entities. The code is clean and follows consistent patterns with other orchestrators.
Summary by CodeRabbit
New Features
Tests
Documentation